package com.pokermind.operator;

import com.pokermind.model.dto.UserStatisticsResponse;
import com.pokermind.model.po.PokerUser;
import com.pokermind.model.po.PokerHand;
import com.pokermind.repository.dao.PokerUserDao;
import com.pokermind.repository.dao.PokerHandDao;
import com.pokermind.repository.dao.PokerHandActionDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 统计分析业务操作类 实现5.4-5.5 查询和统计分析功能
 *
 * @author PokerMind Team
 */
@Service
@Transactional(readOnly = true)
public class StatisticsOperator {

    @Autowired
    private PokerUserDao pokerUserDao;

    @Autowired
    private PokerHandDao pokerHandDao;

    @Autowired
    private PokerHandActionDao pokerHandActionDao;

    /**
     * 5.5.1 获取用户统计概览
     */
    public UserStatisticsResponse getUserStatistics(Long userId, LocalDate dateFrom, LocalDate dateTo,
        String tableType) {
        // 验证用户存在
        PokerUser user = pokerUserDao.findById(userId).orElse(null);
        if (user == null) {
            throw new IllegalArgumentException("User not found: " + userId);
        }

        UserStatisticsResponse response = new UserStatisticsResponse();

        // 获取用户手牌数据
        List<PokerHand> hands = getFilteredHands(userId, dateFrom, dateTo, tableType);

        // 计算总体统计
        UserStatisticsResponse.OverallStats overallStats = calculateOverallStats(hands);
        response.setOverallStats(overallStats);

        // 计算位置统计
        Map<String, UserStatisticsResponse.PositionStats> positionStats = calculatePositionStats(hands);
        response.setPositionStats(positionStats);

        // 计算月度趋势
        List<UserStatisticsResponse.MonthlyTrend> monthlyTrend = calculateMonthlyTrend(hands);
        response.setMonthlyTrend(monthlyTrend);

        // 计算性能指标
        UserStatisticsResponse.PerformanceMetrics performanceMetrics = calculatePerformanceMetrics(hands);
        response.setPerformanceMetrics(performanceMetrics);

        return response;
    }

    /**
     * 5.5.2 获取最近手牌列表
     */
    public Map<String, Object> getRecentHands(Long userId, String tableId, LocalDate dateFrom, LocalDate dateTo,
        String resultFilter, int page, int limit) {
        // 验证分页参数
        if (page < 1)
            page = 1;
        if (limit < 1 || limit > 100)
            limit = 20;

        // 计算偏移量
        int offset = (page - 1) * limit;

        // 获取筛选后的手牌
        List<PokerHand> allHands = getFilteredHands(userId, dateFrom, dateTo, tableId);

        // 应用结果筛选
        if (resultFilter != null) {
            allHands = applyResultFilter(allHands, resultFilter);
        }

        // 按时间倒序排序
        allHands.sort((h1, h2) -> h2.getCreateTime().compareTo(h1.getCreateTime()));

        // 分页处理
        int total = allHands.size();
        List<PokerHand> pagedHands = allHands.stream().skip(offset).limit(limit).collect(Collectors.toList());

        // 转换为响应格式
        List<Map<String, Object>> handItems =
            pagedHands.stream().map(this::convertHandToItem).collect(Collectors.toList());

        // 构建响应
        Map<String, Object> response = new HashMap<>();
        response.put("items", handItems);
        response.put("pagination", createPaginationInfo(page, limit, total));

        return response;
    }

    /**
     * 获取手牌详情
     */
    public Map<String, Object> getHandDetail(Long handRecordId, boolean includeActions, boolean includeAnalysis,
        boolean includeStatistics) {
        PokerHand hand = pokerHandDao.findById(handRecordId).orElse(null);
        if (hand == null) {
            throw new IllegalArgumentException("Hand not found: " + handRecordId);
        }

        Map<String, Object> handDetail = new HashMap<>();

        // 基本手牌信息
        Map<String, Object> handInfo = new HashMap<>();
        handInfo.put("hand_record_id", hand.getId());
        handInfo.put("hand_id", hand.getHandId());
        handInfo.put("table_id", hand.getTableId());
        handInfo.put("current_street", hand.getCurrentStreet());
        handInfo.put("status", hand.getCompletedTime() != null ? "completed" : "active");
        handInfo.put("pot_size", hand.getCurrentPot());
        handInfo.put("board_cards", parseBoardCards(hand.getBoardCards()));
        handDetail.put("hand_info", handInfo);

        // Hero信息
        Map<String, Object> heroInfo = new HashMap<>();
        heroInfo.put("seat_number", hand.getHeroSeatNumber());
        heroInfo.put("position", hand.getHeroPosition());
        heroInfo.put("stack", getCurrentStack(hand));
        heroInfo.put("hole_cards", parseHoleCards(hand.getHeroHoleCards()));
        heroInfo.put("is_to_act", determineIfToAct(hand));
        handDetail.put("hero_info", heroInfo);

        // 包含行动历史
        if (includeActions) {
            handDetail.put("actions", getHandActions(handRecordId));
        }

        // 包含AI分析
        if (includeAnalysis) {
            handDetail.put("ai_analysis", getHandAIAnalysis(handRecordId));
        }

        // 包含统计数据
        if (includeStatistics) {
            handDetail.put("statistics", getHandStatistics(hand));
        }

        return handDetail;
    }

    /**
     * 获取系统总体统计
     */
    public Map<String, Object> getSystemStatistics() {
        Map<String, Object> stats = new HashMap<>();

        // 用户统计
        Map<String, Object> userStats = new HashMap<>();
        userStats.put("total_users", getTotalUsersCount());
        userStats.put("active_users_today", getActiveUsersCount(1));
        userStats.put("active_users_week", getActiveUsersCount(7));
        userStats.put("new_registrations_today", getNewRegistrationsCount(1));
        stats.put("user_statistics", userStats);

        // 手牌统计
        Map<String, Object> handStats = new HashMap<>();
        handStats.put("total_hands", getTotalHandsCount());
        handStats.put("hands_today", getHandsCount(1));
        handStats.put("hands_this_week", getHandsCount(7));
        handStats.put("average_hands_per_user", calculateAverageHandsPerUser());
        stats.put("hand_statistics", handStats);

        // 游戏统计
        Map<String, Object> gameStats = new HashMap<>();
        gameStats.put("total_volume", getTotalGameVolume());
        gameStats.put("average_pot_size", getAveragePotSize());
        gameStats.put("most_popular_stakes", getMostPopularStakes());
        gameStats.put("peak_hours", getPeakHours());
        stats.put("game_statistics", gameStats);

        return stats;
    }

    // 私有辅助方法

    private List<PokerHand> getFilteredHands(Long userId, LocalDate dateFrom, LocalDate dateTo, String tableType) {
        // TODO: 实现数据库查询逻辑，根据参数筛选手牌
        // 这里返回模拟数据
        return generateMockHands(userId, 50);
    }

    private UserStatisticsResponse.OverallStats calculateOverallStats(List<PokerHand> hands) {
        UserStatisticsResponse.OverallStats stats = new UserStatisticsResponse.OverallStats();

        if (hands.isEmpty()) {
            return stats;
        }

        stats.setTotalHands(hands.size());

        // 计算总盈利
        BigDecimal totalProfit =
            hands.stream().filter(h -> h.getHeroNetResult() != null).map(PokerHand::getHeroNetResult)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        stats.setTotalProfit(totalProfit);

        // 计算胜率
        long winningHands = hands.stream()
            .filter(h -> h.getHeroNetResult() != null && h.getHeroNetResult().compareTo(BigDecimal.ZERO) > 0).count();
        BigDecimal winRate =
            BigDecimal.valueOf(winningHands).divide(BigDecimal.valueOf(hands.size()), 3, RoundingMode.HALF_UP);
        stats.setWinRate(winRate);

        // 计算平均底池大小
        BigDecimal avgPotSize = hands.stream().filter(h -> h.getCurrentPot() != null).map(PokerHand::getCurrentPot)
            .reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(hands.size()), 2, RoundingMode.HALF_UP);
        stats.setAvgPotSize(avgPotSize);

        // 计算BB/100
        BigDecimal bbPer100 = totalProfit.multiply(BigDecimal.valueOf(100))
            .divide(BigDecimal.valueOf(hands.size()), 2, RoundingMode.HALF_UP);
        stats.setBbPer100(bbPer100);

        // 估算游戏时间（每手牌5分钟）
        BigDecimal hoursPlayed = BigDecimal.valueOf(hands.size() * 5.0 / 60.0).setScale(1, RoundingMode.HALF_UP);
        stats.setHoursPlayed(hoursPlayed);

        // 计算ROI
        BigDecimal investment = BigDecimal.valueOf(hands.size() * 200); // 假设每手牌平均投入200
        if (investment.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal roi = totalProfit.divide(investment, 3, RoundingMode.HALF_UP);
            stats.setRoi(roi);
        }

        // 计算方差
        stats.setVariance(calculateVariance(hands));

        return stats;
    }

    private Map<String, UserStatisticsResponse.PositionStats> calculatePositionStats(List<PokerHand> hands) {
        Map<String, UserStatisticsResponse.PositionStats> positionStats = new HashMap<>();

        // 按位置分组
        Map<String, List<PokerHand>> handsByPosition = hands.stream().filter(h -> h.getHeroPosition() != null)
            .collect(Collectors.groupingBy(PokerHand::getHeroPosition));

        for (Map.Entry<String, List<PokerHand>> entry : handsByPosition.entrySet()) {
            String position = entry.getKey();
            List<PokerHand> positionHands = entry.getValue();

            UserStatisticsResponse.PositionStats stats = new UserStatisticsResponse.PositionStats();
            stats.setHands(positionHands.size());

            // 计算盈利
            BigDecimal profit =
                positionHands.stream().filter(h -> h.getHeroNetResult() != null).map(PokerHand::getHeroNetResult)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            stats.setProfit(profit);

            // 模拟统计数据
            stats.setVpip(BigDecimal.valueOf(25.5 + Math.random() * 10));
            stats.setPfr(BigDecimal.valueOf(18.2 + Math.random() * 8));
            stats.setThreeBet(BigDecimal.valueOf(5.5 + Math.random() * 3));
            stats.setCbet(BigDecimal.valueOf(65.0 + Math.random() * 15));

            // 计算胜率
            long winningHands = positionHands.stream()
                .filter(h -> h.getHeroNetResult() != null && h.getHeroNetResult().compareTo(BigDecimal.ZERO) > 0)
                .count();
            BigDecimal winRate = BigDecimal.valueOf(winningHands)
                .divide(BigDecimal.valueOf(positionHands.size()), 3, RoundingMode.HALF_UP);
            stats.setWinRate(winRate);

            positionStats.put(position, stats);
        }

        return positionStats;
    }

    private List<UserStatisticsResponse.MonthlyTrend> calculateMonthlyTrend(List<PokerHand> hands) {
        // 按月分组
        Map<String, List<PokerHand>> handsByMonth =
            hands.stream().filter(h -> h.getCreateTime() != null).collect(Collectors.groupingBy(h -> {
                LocalDateTime dateTime =
                    LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(h.getCreateTime()), ZoneId.systemDefault());
                return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM"));
            }));

        return handsByMonth.entrySet().stream().map(entry -> {
            String month = entry.getKey();
            List<PokerHand> monthHands = entry.getValue();

            UserStatisticsResponse.MonthlyTrend trend = new UserStatisticsResponse.MonthlyTrend();
            trend.setMonth(month);
            trend.setHands(monthHands.size());

            BigDecimal profit =
                monthHands.stream().filter(h -> h.getHeroNetResult() != null).map(PokerHand::getHeroNetResult)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            trend.setProfit(profit);

            long winningHands = monthHands.stream()
                .filter(h -> h.getHeroNetResult() != null && h.getHeroNetResult().compareTo(BigDecimal.ZERO) > 0)
                .count();
            BigDecimal winRate =
                BigDecimal.valueOf(winningHands).divide(BigDecimal.valueOf(monthHands.size()), 3, RoundingMode.HALF_UP);
            trend.setWinRate(winRate);

            return trend;
        }).sorted((t1, t2) -> t1.getMonth().compareTo(t2.getMonth())).collect(Collectors.toList());
    }

    private UserStatisticsResponse.PerformanceMetrics calculatePerformanceMetrics(List<PokerHand> hands) {
        UserStatisticsResponse.PerformanceMetrics metrics = new UserStatisticsResponse.PerformanceMetrics();

        // 模拟性能指标计算
        metrics.setShowdownWinRate(BigDecimal.valueOf(0.52));
        metrics.setNonShowdownWinRate(BigDecimal.valueOf(0.48));
        metrics.setPreflopAggression(BigDecimal.valueOf(0.23));
        metrics.setPostflopAggression(BigDecimal.valueOf(0.31));
        metrics.setFoldToCbet(BigDecimal.valueOf(0.42));
        metrics.setStealSuccess(BigDecimal.valueOf(0.38));

        return metrics;
    }

    private BigDecimal calculateVariance(List<PokerHand> hands) {
        if (hands.size() < 2) {
            return BigDecimal.ZERO;
        }

        // 计算平均盈利
        BigDecimal mean = hands.stream().filter(h -> h.getHeroNetResult() != null).map(PokerHand::getHeroNetResult)
            .reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(hands.size()), 4, RoundingMode.HALF_UP);

        // 计算方差
        BigDecimal variance = hands.stream().filter(h -> h.getHeroNetResult() != null)
            .map(h -> h.getHeroNetResult().subtract(mean).pow(2)).reduce(BigDecimal.ZERO, BigDecimal::add)
            .divide(BigDecimal.valueOf(hands.size() - 1), 4, RoundingMode.HALF_UP);

        return variance;
    }

    private List<PokerHand> generateMockHands(Long userId, int count) {
        List<PokerHand> hands = new ArrayList<>();
        Random random = new Random();

        for (int i = 0; i < count; i++) {
            PokerHand hand = new PokerHand();
            hand.setId((long)i);
            hand.setUserId(userId);
            hand.setHeroPosition(getRandomPosition());
            hand.setHeroNetResult(BigDecimal.valueOf((random.nextGaussian() * 100)));
            hand.setCurrentPot(BigDecimal.valueOf(500 + random.nextInt(2000)));
            hand.setCreateTime(
                LocalDate.now().minusDays(random.nextInt(30)).atStartOfDay().atZone(ZoneId.systemDefault()).toInstant()
                    .toEpochMilli());
            hands.add(hand);
        }

        return hands;
    }

    private String getRandomPosition() {
        String[] positions = {"BTN", "SB", "BB", "UTG", "MP", "CO"};
        return positions[new Random().nextInt(positions.length)];
    }

    private List<PokerHand> applyResultFilter(List<PokerHand> hands, String resultFilter) {
        switch (resultFilter) {
            case "win":
                return hands.stream()
                    .filter(h -> h.getHeroNetResult() != null && h.getHeroNetResult().compareTo(BigDecimal.ZERO) > 0)
                    .collect(Collectors.toList());
            case "lose":
                return hands.stream()
                    .filter(h -> h.getHeroNetResult() != null && h.getHeroNetResult().compareTo(BigDecimal.ZERO) < 0)
                    .collect(Collectors.toList());
            case "break_even":
                return hands.stream()
                    .filter(h -> h.getHeroNetResult() != null && h.getHeroNetResult().compareTo(BigDecimal.ZERO) == 0)
                    .collect(Collectors.toList());
            default:
                return hands;
        }
    }

    private Map<String, Object> convertHandToItem(PokerHand hand) {
        Map<String, Object> item = new HashMap<>();
        item.put("hand_record_id", hand.getId());
        item.put("hand_id", hand.getHandId());
        item.put("table_id", hand.getTableId());
        item.put("hero_position", hand.getHeroPosition());
        item.put("net_result", hand.getHeroNetResult());
        item.put("final_pot", hand.getCurrentPot());
        item.put("went_to_showdown", hand.getHeroWentToShowdown());
        item.put("started_at", hand.getCreateTime());
        item.put("completed_at", hand.getCompletedTime());
        return item;
    }

    private Map<String, Object> createPaginationInfo(int page, int limit, long total) {
        Map<String, Object> pagination = new HashMap<>();
        pagination.put("page", page);
        pagination.put("limit", limit);
        pagination.put("total", total);
        pagination.put("pages", (total + limit - 1) / limit);
        pagination.put("has_prev", page > 1);
        pagination.put("has_next", page * limit < total);
        return pagination;
    }

    // 其他辅助方法的模拟实现
    private List<String> parseBoardCards(String boardCardsJson) {
        return new ArrayList<>();
    }

    private List<String> parseHoleCards(String holeCardsJson) {
        return new ArrayList<>();
    }

    private BigDecimal getCurrentStack(PokerHand hand) {
        return BigDecimal.valueOf(18500);
    }

    private boolean determineIfToAct(PokerHand hand) {
        return false;
    }

    private List<Object> getHandActions(Long handRecordId) {
        return new ArrayList<>();
    }

    private List<Object> getHandAIAnalysis(Long handRecordId) {
        return new ArrayList<>();
    }

    private Map<String, Object> getHandStatistics(PokerHand hand) {
        return new HashMap<>();
    }

    // 系统统计的模拟方法
    private long getTotalUsersCount() {
        return 5680;
    }

    private long getActiveUsersCount(int days) {
        return 1240;
    }

    private long getNewRegistrationsCount(int days) {
        return 45;
    }

    private long getTotalHandsCount() {
        return 125000;
    }

    private long getHandsCount(int days) {
        return 3500;
    }

    private BigDecimal calculateAverageHandsPerUser() {
        return BigDecimal.valueOf(850);
    }

    private BigDecimal getTotalGameVolume() {
        return BigDecimal.valueOf(2500000);
    }

    private BigDecimal getAveragePotSize() {
        return BigDecimal.valueOf(875);
    }

    private List<String> getMostPopularStakes() {
        return List.of("NL200", "NL500", "NL100");
    }

    private List<String> getPeakHours() {
        return List.of("20:00-22:00", "14:00-16:00");
    }
}